import Link from "next/link"; import { notFound } from "next/navigation"; import { ArrowLeft, LifeBuoy } from "lucide-react"; import { ERROR_MESSAGES, type ErrorCode } from "@fetcha/core"; import { getWorkspace } from "@/lib/session"; import { formatBytes, formatDate, formatMs, formatUsd, timeAgo } from "@/lib/format"; import { getRequestDetail } from "@/lib/queries/dashboard"; import { PageHeader } from "@/components/ui/page-header"; import { Badge, StatusBadge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { CopyButton } from "@/components/ui/copy-button"; import { Alert } from "@/components/ui/alert"; import { RequestDetailTabs } from "@/components/dashboard/requests/request-detail-tabs"; import { TimingWaterfall } from "@/components/dashboard/requests/timing-waterfall"; import { AttemptsTimeline } from "@/components/dashboard/requests/attempts-timeline"; import { HeadersTables } from "@/components/dashboard/requests/headers-table"; import { HttpCode, NetworkLabel } from "@/components/dashboard/requests/requests-table"; export const dynamic = "force-dynamic"; function Row({ label, children, mono }: { label: string; children: React.ReactNode; mono?: boolean }) { return (
{label}
{children}
); } export default async function RequestDetailPage({ params }: { params: Promise<{ id: string }> }) { const [ws, { id }] = await Promise.all([getWorkspace(), params]); if (!/^req_[A-Za-z0-9]{4,64}$/.test(id)) notFound(); const req = await getRequestDetail(ws.organization.id, id, { providerVisibility: ws.organization.providerVisibility }); if (!req) notFound(); const headerCount = Object.keys(req.requestHeaders ?? {}).length + Object.keys(req.responseHeaders ?? {}).length; const errorHelp = req.errorCode && req.errorCode in ERROR_MESSAGES ? ERROR_MESSAGES[req.errorCode as ErrorCode] : null; const otherProject = req.projectId !== ws.project.id; const overview = (
Request
{req.url} {req.finalUrl && req.finalUrl !== req.url ? ( {req.finalUrl} ) : null} {req.method} {req.domain} {req.format} {req.source} {req.browser ? ( browser requested ) : null} {req.projectName} {otherProject ? (not the current project) : null} {req.apiKey ? ( {req.apiKey.name} {req.apiKey.prefix}…{req.apiKey.last4} {req.apiKey.revoked ? revoked : null} ) : ( {req.source === "playground" ? "Playground (no key)" : "—"} )} {req.sessionId ? ( {req.sessionId} ) : ( none )}
Routing and result
requested {req.requestedNetwork} {[req.country, req.region, req.city].filter(Boolean).join(" · ") || any} {formatMs(req.latencyMs)} {req.attempts} {formatBytes(req.bytesIn)} / {formatBytes(req.bytesOut)} {formatUsd(req.priceUsd, true)} {req.cached ? cached : null} {req.errorCode ? ( {req.errorCode} {req.errorMessage ?
{req.errorMessage}
: null}
) : null} {req.completedAt ? formatDate(req.completedAt, { timeStyle: "medium" }) : pending}
); return (
Requests {req.id} } description={ <> {formatDate(req.createdAt, { timeStyle: "medium" })} ({timeAgo(req.createdAt)}) · {req.method} {req.domain} } actions={ } /> {req.status === "failed" && errorHelp ? ( {req.errorMessage ?? errorHelp} {req.errorMessage && req.errorMessage !== errorHelp ? {errorHelp} : null} ) : null}
Attempts Each route the router tried, in order. A blocked route escalates to another network or IP automatically. } headers={} timing={} />
); }